#include "UserManager.h"
#include <algorithm>
#include <fstream>
#include "Logger.h"
#include "User.h"

void UserManager::loadUsers()
{
    // const std::string FILE_NAME = "../data/data.json";
    // std::ifstream     fs(FILE_NAME);
    // if (!fs.is_open()) {
    //     return;
    // }
    // json js;
    // fs >> js;

    // auto users = js["user_data"];
    // for (auto& user : users) {
    //     User u       = user.get<User>();
    //     users_[u.id] = u;
    // }
    LOG_INFO(CONSOLE_LOG) << "ChatEngine load user data from db"_s;
    users_ = db_manager_.getAllUsers();
    for (auto& user : users_) {
        LOG_INFO(CONSOLE_LOG) << "ChatEngine load user data from db"_s << user.second.toJson();
        user_names_.insert(user.second.name);
    }
}

UserLoginRes UserManager::handleUserLoginReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    const std::string content(buf, len);
    auto              req = UserLoginReq::fromJson(content);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine receive login msg  peer addr = "_s
                          << " connid:" << connId << " msg:" << req.toJson();
    if (users_.find(req.id) != users_.end()) {
        auto& user = users_[req.id];
        if (user.isOnline) {
            return UserLoginRes{user.id, connId, user.name, "repeated login"};
        }
        if (user.password == req.password) {
            user.sessionId     = connId;
            user.isOnline      = true;
            user.tid           = tid;
            conn2User_[connId] = &user;
            return UserLoginRes{user.id, connId, user.name, "success"};
        }
        else {
            return UserLoginRes{user.id, connId, user.name, "password error"};
        }
    }
    else {
        return UserLoginRes{0, connId, "", "user not exist"};
    }
    return UserLoginRes{0, connId, "", "unknow error"};
}

UserListRes UserManager::handleUserListReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    const std::string content(buf, len);
    auto              req = UserListReq::fromJson(content);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine receive userlist msg  peer addr = "_s
                          << " connid:" << connId << " msg:" << req.toJson();
    if (users_.find(req.id) != users_.end()) {
        auto& user = users_[req.id];
        if (user.sessionId == req.sessionId) {
            if (req.reqAllUsers) {
                std::vector<simpleUser> friendUsers;
                for (auto& u : users_) {
                    if (u.second.id != req.id &&
                        std::find(user.friends.begin(), user.friends.end(), u.second.id) == user.friends.end()) {
                        friendUsers.push_back(simpleUser{u.second.id, u.second.name, u.second.isOnline});
                    }
                }
                return UserListRes{req.id, req.sessionId, req.reqAllUsers, friendUsers, "success"};
            }
            else {
                std::vector<simpleUser> friendUsers;
                for (auto fid : user.friends) {
                    for (auto& u : users_) {
                        if (u.first == fid) {
                            friendUsers.push_back(simpleUser{u.second.id, u.second.name, u.second.isOnline});
                        }
                    }
                }
                return UserListRes{req.id, req.sessionId, req.reqAllUsers, friendUsers, "success"};
            }
        }
        else {
            return UserListRes{0, req.sessionId, req.reqAllUsers, {}, "sessionId error"};
        }
    }
    else {
        return UserListRes{0, req.sessionId, req.reqAllUsers, {}, "user not found"};
    }
}

UserRegisterRes
UserManager::handleUserRegisterReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    const std::string content(buf, len);
    auto              req = UserRegisterReq::fromJson(content);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine receive register msg  peer addr = "_s
                          << " connid:" << connId << " msg:" << req.toJson();
    if (req.name.empty()) {
        return UserRegisterRes{0, "", 0, "user name is empty"};
    }
    if (req.password.empty()) {
        return UserRegisterRes{0, "", 0, "password is empty"};
    }

    if (user_names_.find(req.name) != user_names_.end()) {
        return UserRegisterRes{0, "", 0, "user name already exist"};
    }
    else {
        User tmpUser;
        tmpUser.name      = req.name;
        tmpUser.password  = req.password;
        tmpUser.isOnline  = false;
        tmpUser.sessionId = connId;
        tmpUser.id        = db_manager_.addUser(tmpUser);
        if (tmpUser.id != -1) {
            users_[tmpUser.id] = tmpUser;
            user_names_.insert(tmpUser.name);
            return UserRegisterRes{tmpUser.id, tmpUser.name, tmpUser.sessionId, "success"};
        }
        else {
            return UserRegisterRes{0, "", 0, "db error"};
        }
    }
    return UserRegisterRes{0, "", 0, "unknow error"};
}

AddDelFriendRes
UserManager::handleAddDelFriendReq(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId)
{
    const std::string content(buf, len);
    auto              req = AddDelFriendReq::fromJson(content);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine receive add friend msg  peer addr = "_s
                          << " connid:" << connId << " msg:" << req.toJson();
    if (users_.find(req.id) != users_.end()) {
        auto& user = users_[req.id];
        if (user.sessionId == req.sessionId) {
            auto to_user = getUser(req.friendId);
            if (to_user != nullptr) {
                if (req.add) {
                    bool ret = db_manager_.addNewFriend(req.id, req.friendId);
                    if (!ret) {
                        return AddDelFriendRes{req.id, req.sessionId, req.friendId, req.add, "db error"};
                    }
                    user.friends.push_back(req.friendId);
                    to_user->friends.push_back(req.id);
                    return AddDelFriendRes{req.id, req.sessionId, req.friendId, req.add, "success"};
                }
                else {
                    bool ret = db_manager_.deleteFriend(req.id, req.friendId);
                    if (!ret) {
                        return AddDelFriendRes{req.id, req.sessionId, req.friendId, req.add, "db error"};
                    }
                    user.friends.erase(std::find(user.friends.begin(), user.friends.end(), req.friendId));
                    to_user->friends.erase(std::find(to_user->friends.begin(), to_user->friends.end(), req.id));
                    return AddDelFriendRes{req.id, req.sessionId, req.friendId, req.add, "success"};
                }
            }
            else {
                return AddDelFriendRes{req.id, req.sessionId, 0, req.add, "friend user not found"};
            }
        }
        else {
            return AddDelFriendRes{req.id, req.sessionId, 0, req.add, "sessionId error"};
        }
    }
    else {
        return AddDelFriendRes{req.id, req.sessionId, 0, req.add, "user not found"};
    }
}

UserChatRes
UserManager::handleUserChatMsg(const char* buf, uint32_t len, thread_id_t tid, connection_id_t connId, UserChatMsg& msg)
{
    const std::string content(buf, len);
    auto              req = UserChatMsg::fromJson(content);
    LOG_INFO(CONSOLE_LOG) << "ChatEngine receive chat msg  peer addr = "_s
                          << " connid:" << connId << " msg:" << req.toJson();
    if (users_.find(req.id) != users_.end()) {
        auto& user = users_[req.id];
        if (user.sessionId == req.sessionId) {
            auto to_user = getUser(req.toId);
            if (to_user != nullptr) {
                if (to_user->isOnline) {
                    msg.toId      = req.id;
                    msg.id        = to_user->id;
                    msg.sessionId = to_user->sessionId;
                    msg.msg       = req.msg;

                    return UserChatRes{
                        req.id,
                        req.sessionId,
                        req.toId,
                        "success",
                    };
                }
                else {
                    return UserChatRes{
                        req.id,
                        req.sessionId,
                        req.toId,
                        "friend user not online",
                    };
                }
            }
            else {
                return UserChatRes{
                    req.id,
                    req.sessionId,
                    req.toId,
                    "friend user not found",
                };
            }
        }
        else {
            return UserChatRes{
                req.id,
                req.sessionId,
                req.toId,
                "sessionId error",
            };
        }
    }
    else {
        return UserChatRes{
            req.id,
            req.sessionId,
            req.toId,
            "user not found",
        };
    }

    return UserChatRes{
        req.id,
        req.sessionId,
        req.toId,
        "unknow error",
    };
}

const User* UserManager::getUser(user_id_t id) const
{
    auto iter = users_.find(id);
    if (iter != users_.end()) {
        return &iter->second;
    }
    else {
        return nullptr;
    }
}

User* UserManager::getUser(user_id_t id)
{
    auto iter = users_.find(id);
    if (iter != users_.end()) {
        return &iter->second;
    }
    else {
        return nullptr;
    }
}

user_id_t UserManager::handleDisconnected(connection_id_t id, thread_id_t tid)
{
    auto iter = conn2User_.find(id);
    if (iter != conn2User_.end()) {
        auto user      = iter->second;
        user->isOnline = false;
        conn2User_.erase(iter);
        return user->id;
    }
    return 0;
}
